#include <iostream>
#include <thread>
#include <mutex>
#include <atomic>
#include <future>
#include <chrono>

using namespace std;
using namespace std::literals;

#define WORDWARP "\r\n"

//no param thread
#if 0
void dotest() { std::cout << "World"; }

int main(int argc, char** argv)
{
	std::thread _ta([]() {std::cout << "Hello,"; }), _tb(::dotest);
	_ta.join();
	_tb.join();
}
#endif // 0

//thread with parameter
#if 0

void CounterNumber(int id, unsigned int n)
{
	for (unsigned int i = 0; i < n; ++i)
		cout << "thread " << id << "finished!" << endl;
}

int main(int argc, char** argv)
{
	thread _th[10]{};
	for (int i = 0; i < 10; ++i)
		_th[i] = thread(CounterNumber, i, 1000);
	for (int i = 0; i < 10; ++i)
		_th[i].join();
}

#endif // 1

//thread with reference parameter
#if 0
template <typename T>
void ChangeValue(T& x, T val) {
	x = val;
	cout << __func__ << "x: " << x << endl;
}

int main(void)
{
	thread _th[100]{};
	int num[100];
	for (int i = 0; i < 100; ++i)
		_th[i] = thread(ChangeValue<int>, std::ref(num[i]), i + 1);
	for (int i = 0; i < 100; ++i)
		_th[i].join();
}
#endif // 1

//thread mutex
#if 0
int n = 0;
mutex g_mutex{};
void Counter1000()
{
	for (int i = 0; i < 1000; ++i)
	{
		g_mutex.lock();
		n++;
		g_mutex.unlock();
	}		
}

int main(void)
{
	thread _th[100];
	for (auto& _thobj : _th)
		_thobj = thread(Counter1000);
	for (auto& _obj : _th)
		_obj.join();
	cout << "n= " << n;
}
#endif // 1

//atomic
#if 0
atomic<int> n = 0;
void Counter1000() {
	for (int i = 0; i < 1000; ++i)
		n++;
}

int main(void)
{
	thread _th[100]{};
	for (auto& x : _th)
		x = thread(Counter1000);
	for (auto& x : _th)
		x.join();
	cout << "n = " << n;
}
#endif // 1

//future no block
#if 0
void display(const char*msg) {
	cout <<msg << " World";
}

int main(void)
{
	async(launch::async, display,"Hello");
}
#endif // 1

//future block
#if 0
template<typename ... Args>
decltype(auto) DefSum(Args &&...args) {
	return (0 + ... + args);
}

int main(void)
{
	auto _value = async(launch::async, DefSum<int, int, int>, 1, 10, 100);
	cout << _value.get() << endl;
}
#endif // 1

//future with special
#if 0
void CountBigNumber() {
	for (int i = 0; i < 1'000'000'000; ++i);
}


int main(void)
{
	auto _start = std::chrono::steady_clock::now();
	auto _future = async(std::launch::async, CountBigNumber);
	cout << "Please wait" << flush;
	while (_future.wait_for(std::chrono::seconds(1)) != std::future_status::ready)
		cout << "." << flush;
	auto _end = std::chrono::steady_clock::now();
	cout << std::chrono::duration_cast<std::chrono::milliseconds>(_end - _start).count() << " ms" << endl;
	cout << endl << "Finished!" << endl;
}
#endif // 1

//promise
#if 0
template <typename ...Args>
decltype(auto)DefSum(Args...args) {
	return (0 + ... + args);
}

template<class ...Args>
void SumThread(promise<long>& val, Args&& ...args)
{
	val.set_value(DefSum(args...));
}

int main(void)
{
	std::promise<long>_sum_value{};
	thread get_sum(SumThread<int,int,int>,std::ref(_sum_value),1,10,100);
	cout << _sum_value.get_future().get() << endl;
	get_sum.join();
}
#endif // 1


//jthread
#if 0

int main(void)
{
	std::cout << std::boolalpha;

	jthread _thread{ [] {cout << "thread id: " << this_thread::get_id << endl; }};
	std::cout << "joinable: " << _thread.joinable() << endl;
	
}
#endif // 1

//jthread stop token
#if 0
int main(void)
{
	cout << "\n";
	std::jthread noninterruptable([] {
		int counter{ 0 };
		while (10 > counter) {
			std::this_thread::sleep_for(std::chrono::milliseconds(200));
			std::cerr << "nointerruptable: " << counter << "\r\n";
			++counter;
		}
		});
	std::jthread interruptable([](std::stop_token stoken) {
		int counter{ 0 };
		while (10 > counter) {
			std::this_thread::sleep_for(0.2s);
			if (stoken.stop_requested())
				return;
			std::cerr << "interruptable: " << counter << "\r\n";
			++counter;
		}
		});
	std::this_thread::sleep_for(std::chrono::seconds(1));
	std::cerr << "\r\n";
	std::cerr << "main thread interrupts both jthreads" << WORDWARP;
	noninterruptable.request_stop();
	interruptable.request_stop();
	cout << WORDWARP;
}
#endif // 1

//jthread stop resource
#if 0
int main(void)
{
	std::cout << boolalpha;
	auto print = [](std::string_view name, const std::stop_source& source) {
		cout << name << " :stop_possible=" << source.stop_possible();
		cout << ",stop_request=" << source.stop_requested() << endl;
		};

	auto worker = std::jthread([](std::stop_token stoken) {
		for (int i = 10; i; --i) {
			std::this_thread::sleep_for(std::chrono::milliseconds(300));
			if (stoken.stop_requested()) {
				cout << " Sleepy worker is requested to stop" << WORDWARP;
				return;
			}
			cout << "sleepy worker goes back to sleep" << endl;
		}
		});
	std::stop_source ssource = worker.get_stop_source();
	print("stop_resource", ssource);

	cout << WORDWARP << "Pass source to other thread" << WORDWARP;
	auto stoper = thread([](std::stop_source source) {
		std::this_thread::sleep_for(500ms);
		std::cout << "Request stop for worker via source" << WORDWARP;
		source.request_stop();
		}, ssource);
	stoper.join();
	std::this_thread::sleep_for(std::chrono::milliseconds(200));
	std::cout << WORDWARP;
	print("stop_source", ssource);
}
#endif // 1

//jthread stop
#if 1
int main() {
	auto f = [](const std::stop_token stoken) {
		while (!stoken.stop_requested())
		{
			std::cout << "other :" << std::this_thread::get_id() << WORDWARP;
			std::this_thread::sleep_for(std::chrono::milliseconds(1000));
		}
		cout << "other thread stopped!" << WORDWARP;
		};
	std::jthread jth(f);
	std::cout << "main: " << std::this_thread::get_id() << WORDWARP;
	std::this_thread::sleep_for(5s);
	jth.request_stop();
}
#endif // 1


